<?php

Raise::load('core.image.RaiseImage');
Raise::load('core.image.RaiseImageResource');
Raise::load('core.exception.RaiseLastError');

/**
 * RaiseImageEditor class
 * Provides functionality for drawing an image
 *
 * @author Sam-Mauris Yong / hellclanner at live dot com
 * @license http://www.opensource.org/licenses/bsd-license New BSD License
 * @package phpraise.core.image
 * @since 1.2
 */
class RaiseImageEditor extends RaiseImageResource {

    /**
     * Create a RaiseImageEditor from a RaiseImage
     * @param RaiseImage $image The image to edit
     */
    public function __construct($image) {
        $this->resource($image->resource());
    }

    /**
     * Set the thickness of all the lines drawn
     * @param integer $thickness The thickness of the lines in pixels
     */
    public function lineThickness($thickness) {
        $ok = imagesetthickness($this->resource(), $thickness);
        if (!$ok) {
            $lastError = RaiseLastError::error();
            $lastError = $lastError ? $lastError : 'Unknown Error';
            throw new RaiseImageException(sprinf('RaiseImageEditor::lineThickness() failed to set image line thickness: %s', $lastError));
        }
    }

    /**
     * Set the style of all the lines drawn
     * @param RaiseCollection|array $style The style of the lines
     */
    public function lineStyle($style) {
        if($style instanceof RaiseCollection){
            $style = $style->toArray();
        }
        $ok = imagesetstyle($this->resource(), $style);
        if (!$ok) {
            $lastError = RaiseLastError::error();
            $lastError = $lastError ? $lastError : 'Unknown Error';
            throw new RaiseImageException(sprinf('RaiseImageEditor::lineStyle() failed to set image line style: %s', $lastError));
        }
    }

    /**
     * Set to use another image as the brush for all the lines drawn
     * @param RaiseImage $image The image to set as the brush
     */
    public function lineBrush($image){
        if(!($image instanceof RaiseImage || $image === null)){
            throw new RaiseInvalidArgumentException('RaiseImageEditor::lineBrush() expects first parameter $image to be of type RaiseImage or NULL.');
        }
        $ok = imagesetbrush($this->resource(), $image ? $image->resource() : null);
        if (!$ok) {
            $lastError = RaiseLastError::error();
            $lastError = $lastError ? $lastError : 'Unknown Error';
            throw new RaiseImageException(sprinf('RaiseImageEditor::lineBrush() failed to set image line brush: %s', $lastError));
        }
    }

    /**
     * Set to use another image as the tiling fill for all filling colors
     * @param RaiseImage $image The image to set as the tiling fill
     */
    public function fillTile($image){
        if(!($image instanceof RaiseImage || $image === null)){
            throw new RaiseInvalidArgumentException('RaiseImageEditor::fillTile() expects first parameter $image to be of type RaiseImage or NULL.');
        }
        $ok = imagesetbrush($this->resource(), $image ? $image->resource() : null);
        if (!$ok) {
            $lastError = RaiseLastError::error();
            $lastError = $lastError ? $lastError : 'Unknown Error';
            throw new RaiseImageException(sprinf('RaiseImageEditor::fillTile() failed to set image fill tile: %s', $lastError));
        }
    }

    /**
     * Paste an RaiseImage onto this RaiseImage at a specific coordinate
     * @param RaiseImage $img The image to paste
     * @param RaisePoint The coordinates of the $img to paste at on this image.
     */
    public function drawImage($img, $point) {
        if (!($img instanceof RaiseImage)) {
            throw new RaiseInvalidArgumentException('RaiseImageEditor::drawImage() expects $img to be a RaiseImage object.');
        }
        imagecopyresampled($img->resource(), $this->resource(), 0, 0, $point->x(), $point->y(), $img->width(), $img->height(), $img->width(), $img->height());
    }

    /**
     * Draw a string of characters on the image.
     * @param RaiseImageFont $font
     * @param RaisePoint $position
     * @param string|RaiseString $string
     * @param RaiseColor $color
     */
    public function drawString($font, $position, $string, $color, $vertical = false) {
        $colorId = $this->allocateColor($color);
        if ($vertical) {
            imagestring($this->resource(), $font->value(), $position->x(), $position->y(), $string, $colorId);
        } else {
            imagestringup($this->resource(), $font->value(), $position->x(), $position->y(), $string, $colorId);
        }
    }

    /**
     * Draw a rectangle with optional border and fill color
     * @param RaiseRectangle $rect The coordinates of the rectangle to draw. All units are in pixels
     * @param RaiseColor $fillColor (optional) The fill color of the rectangle
     * @param RaiseColor $borderColor (optional) The border color of the rectangle
     */
    public function drawRectangle($rect, $fillColor = null, $borderColor = null) {
        if ($fillColor) {
            $fillColorId = $this->allocateColor($fillColor);
            imagefilledrectangle($this->resource(), $rect->x(), $rect->y(), $rect->x2(), $rect->y2(), $fillColorId);
        }
        if ($borderColor) {
            $borderColorId = $this->allocateColor($borderColor);
            imagerectangle($this->resource(), $rect->x(), $rect->y(), $rect->x2(), $rect->y2(), $borderColorId);
        }
    }

    /**
     * Draw a ellipse with optional border and fill color
     * @param RaisePoint $center The position of the center of ellipse to draw. All units are in pixels
     * @param integer $width The width of the ellipse in pixels
     * @param integer $height The height of the ellipse in pixels
     * @param RaiseColor $fillColor (optional) The fill color of the ellipse
     * @param RaiseColor $borderColor (optional) The border color of the ellipse
     */
    public function drawEllipse($center, $width, $height, $fillColor = null, $borderColor = null) {
        if ($fillColor) {
            $fillColorId = $this->allocateColor($fillColor);
            imagefilledellipse($this->resource(), $center->x(), $center->y(), $width, $height, $fillColorId);
        }
        if ($borderColor) {
            $borderColorId = $this->allocateColor($borderColor);
            imageellipse($this->resource(), $center->x(), $center->y(), $width, $height, $borderColorId);
        }
    }

    /**
     * Draw an arc with optional border and fill color
     * @param RaisePoint $center The position of the center of arc to draw. All units are in pixels
     * @param integer $width The width of the arc in pixels
     * @param integer $height The height of the arc in pixels
     * @param integer $start The starting angle of the arc in degrees. 0° is located at the three-o'clock position, and the arc is drawn clockwise.
     * @param integer $end The ending angle of the arc in degrees. 0° is located at the three-o'clock position, and the arc is drawn clockwise.
     * @param RaiseColor $fillColor (optional) The fill color of the ellipse
     * @param RaiseColor $borderColor (optional) The border color of the ellipse
     */
    public function drawArc($center, $width, $height, $start, $end, $fillColor = null, $borderColor = null) {
        if ($fillColor) {
            $fillColorId = $this->allocateColor($fillColor);
            imagefilledarc($this->resource(), $center->x(), $center->y(), $width, $height, $start, $end, $fillColorId);
        }
        if ($borderColor) {
            $borderColorId = $this->allocateColor($borderColor);
            imagearc($this->resource(), $center->x(), $center->y(), $width, $height, $start, $end, $borderColorId);
        }
    }

    /**
     * Draw a polygon with optional border and fill color
     * @param RaiseCollection|array $positions The coordinates of the polygon to draw. All units are in pixels
     * @param RaiseColor $fillColor (optional) The fill color of the rectangle
     * @param RaiseColor $borderColor (optional) The border color of the rectangle
     */
    public function drawPolygon($positions, $fillColor = null, $borderColor = null) {
        $numPoints = 0;
        if($positions instanceof RaiseTypedCollection && $positions->type() == 'RaisePoint'){
            $p = $positions;
            $positions = array();
            foreach($p as $l){
                $positions[] = $l->x();
                $positions[] = $l->y();
            }
        }
        if($positions instanceof RaiseCollection){
            $positions = $positions->toArray();
        }
        $numPoints = count($positions) / 2;
        if ($fillColor) {
            $fillColorId = $this->allocateColor($fillColor);
            imagefilledpolygon($this->resource(), $positions, $numPoints, $fillColorId);
        }
        if ($borderColor) {
            $borderColorId = $this->allocateColor($borderColor);
            imagepolygon($this->resource(), $positions, $numPoints, $borderColorId);
        }
    }

    /**
     * Draw a circle with optional border and fill color
     * @param RaisePoint $center The position of the center of circle to draw. All units are in pixels
     * @param integer $width The width of the circle in pixels
     * @param integer $height The height of the circle in pixels
     * @param RaiseColor $fillColor (optional) The fill color of the circle
     * @param RaiseColor $borderColor (optional) The border color of the circle
     */
    public function drawCircle($center, $radius, $fillColor = null, $borderColor = null) {
        $height = $radius * 2;
        $width = $radius * 2;
        $this->drawEllipse($center, $width, $height, $fillColor, $borderColor);
    }

    /**
     * Draw a line on the image
     * @param RaisePoint $a The first position of the line in pixels
     * @param RaisePoint $b The second position of the line in pixels
     * @param RaiseColor $color The color of the line
     */
    public function drawLine($a, $b, $color) {
        $colorId = $this->allocateColor($color);
        imageline($this->resource(), $a->x(), $a->y(), $b->x(), $b->y(), $colorId);
    }

    /**
     * Allocate color in the image
     * @param RaiseColor $color
     * @return integer The allocated color identifier
     */
    private function allocateColor($color) {
        if (is_int($color)) {
            return $color;
        }
        if ($color->alpha()) {
            return imagecolorallocatealpha($this->resource(), $color->red(), $color->green(), $color->blue(), $color->alpha());
        } else {
            return imagecolorallocate($this->resource(), $color->red(), $color->green(), $color->blue());
        }
    }

}